package com.anb.common;

import java.util.*;

public class Timeout {

    // 100ms
    public static final long DEFAULT_INTERVAL = 100;

    public Timeout(long interval) {
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                check();
            }
        }, 0, interval);
    }

    public static Timeout instance()
    {
        if (instance == null) {
            synchronized (Timeout.class) {
                if (instance == null) {
                    instance = new Timeout();
                }
            }
        }
        return instance;
    }

    private static Timeout instance;

    private Timer timer;

    public Timeout() {
        this(DEFAULT_INTERVAL);
    }

    public interface Callback
    {
        void timeout() throws Exception;
    }

    private SortedMap<Long, Callback> callbacks = new TreeMap<Long, Callback>();


    public void check() {
        long ns = System.nanoTime();
        List<Callback> timeouts = new ArrayList<Callback>();
        synchronized (callbacks) {
            while (!callbacks.isEmpty()) {
                Long first = callbacks.firstKey();
                if (first > ns) {
                    break;
                }
                timeouts.add(callbacks.remove(first));
            }
        }
        for (Callback callback : timeouts) {
            try {
                callback.timeout();
            } catch (Exception e) {
                Log.Get().error("timeout callback exception", e);
            }
        }
    }

    public static final long NS_OF_MS = 1000 * 1000;
    public long add(long ms, Callback callback) {
        long ns = ms * NS_OF_MS + System.nanoTime();
        synchronized (callbacks) {
            return add1(ns, callback);
        }
    }

    private long add1(long key, Callback callback) {
        if (callbacks.containsKey(key)) {
            return add1(key + 1, callback);
        }
        callbacks.put(key, callback);
        return key;
    }

    public void remove(long key) {
        synchronized (callbacks) {
            callbacks.remove(key);
        }
    }

    public void stop() {
        callbacks.clear();
        timer.cancel();
    }
}
